#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2025/9/15 10:00
# @Author  : MINGY
# @File    : jbossScan.py
# @Software: VSCode
# @Description: JBoss漏洞扫描工具

import sys
import requests
import argparse
import urllib3
from urllib.parse import urljoin

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3"
}

# 全局变量，用于存储输出文件对象
output_file = None

# 漏洞路径列表
vul_paths = {
    '/jmx-console': 'JMX Console',
    '/web-console': 'Web Console', 
    '/admin-console': 'Admin Console',
    '/web-console/Invoker': 'Web Console Invoker',
    '/status': 'Status Page',
    '/cluster': 'Cluster View',
    'jmx-console/HtmlAdaptor?action=inspectMBean&name=jboss.deployment:type=DeploymentScanner,flavor=URL': 'CVE-2007-1036 (JMX Console HtmlAdaptor)',
    '/invoker/EJBInvokerServlet': 'CVE-2013-4810 (EJBInvokerServlet)',
    '/invoker/JMXInvokerServlet': 'CVE-2015-7501 (JMXInvokerServlet)',
    '/jbossmq-httpil/HTTPServerILServlet': 'CVE-2017-7504 (JBOSSMQ JMS)',
    '/invoker/readonly': 'CVE-2017-12149 (Deserialization)'
}

# 常见弱口令
weak_credentials = [
    'admin:admin',
    'jboss:admin',
    'admin:jboss',
    'admin:123456',
    'admin:vulhub',
    'admin:password',
    'admin:',
    'admin:admin123',
    'root:root',
    'root:'
]

def write_output(text):
    """写入输出到控制台和文件（如果指定了输出文件）"""
    print(text)
    if output_file:
        output_file.write(text + '\n')
        output_file.flush()

def check_url(url, method='GET'):
    """检查单个URL路径"""
    try:
        if method == 'HEAD':
            r = requests.head(url, headers=headers, timeout=5, verify=False)
        else:
            r = requests.get(url, headers=headers, timeout=5, verify=False)
        return r
    except Exception as e:
        return None

def check_vulnerability(target):
    """检查目标的所有漏洞路径"""
    target = target.strip()
    if not target.startswith(('http://', 'https://')):
        target = 'http://' + target

    write_output("==================== " + target + " ====================\n")

    # 检查各漏洞路径
    for path, description in vul_paths.items():
        url = urljoin(target, path)
        response = check_url(url)
        
        if response is not None:
            # 处理不同状态码
            if response.status_code == 401:
                write_output(f"[+] {description} - Unauthorized access detected!")
                write_output(f"    >>> Access URL: {url}\n")
                # 尝试弱口令
                test_weak_credentials(url, description)
            elif response.status_code == 200:
                write_output(f"[+] {description} - Accessible (Status: 200)")
                write_output(f"    >>> Access URL: {url}\n")
                # 特殊处理某些路径
                if 'JMXInvokerServlet' in path and 'download' in response.text.lower():
                    write_output(f"[+] {description} - Vulnerability confirmed, download prompt detected!")
                    write_output(f"    >>> Access URL: {url}\n")
                elif 'readonly' in path:
                    write_output(f"[+] {description} - Vulnerability likely exists (Status: 500 expected but 200 returned)")
                    write_output(f"    >>> Access URL: {url}\n")
            elif response.status_code == 500 and 'readonly' in path:
                write_output(f"[+] {description} - Vulnerability likely exists (Status: 500)")
                write_output(f"    >>> Access URL: {url}\n")
            elif response.status_code == 404:
                # 路径不存在，不输出
                pass
            else:
                # 其他状态码
                write_output(f"[+] {description} - Returned status code {response.status_code}")
                write_output(f"    >>> Access URL: {url}\n")

    # 检查CVE-2010-0738 (JMX Console安全认证绕过)
    check_cve_2010_0738(target)
    
    # 检查管理接口
    check_management_interfaces(target)

def check_cve_2010_0738(target):
    """检查CVE-2010-0738漏洞, 通过HEAD请求绕过认证"""
    path = '/jmx-console/HtmlAdaptor'
    url = urljoin(target, path)
    description = 'CVE-2010-0738 (JMX Console Auth Bypass)'
    
    # 使用HEAD方法请求
    response = check_url(url, method='HEAD')
    
    if response is not None:
        if response.status_code == 200:
            write_output(f"[+] {description} - Vulnerability detected (HEAD method bypass)")
            write_output(f"    >>> Access URL: {url}\n")
        elif response.status_code == 401:
            write_output(f"[+] {description} - Authentication detected but may be bypassable")
            write_output(f"    >>> Access URL: {url}\n")

def test_weak_credentials(url, description):
    """测试弱口令"""
    parsed_url = urllib3.util.parse_url(url)
    base_url = f"{parsed_url.scheme}://{parsed_url.host}"
    if parsed_url.port:
        base_url += f":{parsed_url.port}"
    
    for credential in weak_credentials:
        username, password = credential.split(':', 1)
        auth = (username, password) if password else (username, '')
        
        try:
            r = requests.get(url, auth=auth, headers=headers, timeout=5, verify=False)
            if r.status_code == 200:
                write_output(f"[!] Weak credential found for {description}: {credential}")
                write_output(f"    >>> Access URL: {url} with {credential}\n")
                break
        except:
            pass

def check_management_interfaces(target):
    """检查管理接口"""
    # JBoss AS 7+ 管理接口
    management_paths = [
        '/management',
        '/console'
    ]
    
    for path in management_paths:
        url = urljoin(target, path)
        response = check_url(url)
        
        if response is not None:
            if response.status_code == 401:
                write_output(f"[+] JBoss Management Interface detected: {url}")
                write_output(f"    >>> Access URL: {url}\n")
                test_weak_credentials(url, "Management Interface")
            elif response.status_code == 200 and len(response.text) > 0:
                write_output(f"[+] JBoss Management Interface accessible: {url}")
                write_output(f"    >>> Access URL: {url}\n")

def main():
    global output_file
    
    parser = argparse.ArgumentParser(description='JBoss漏洞扫描工具 -- By MINGY')
    parser.add_argument('-u', '--url', help='单个目标URL')
    parser.add_argument('-f', '--file', help='目标URL文件')
    parser.add_argument('-o', '--output', help='输出结果到指定文件')
    
    args = parser.parse_args()
    
    # 如果指定了输出文件，则打开文件
    if args.output:
        try:
            output_file = open(args.output, 'w', encoding='utf-8')
        except Exception as e:
            print(f"错误: 无法创建输出文件 {args.output}: {e}")
            sys.exit(1)
    
    targets = []
    
    # 处理命令行参数
    if args.url:
        targets.append(args.url)
    elif args.file:
        try:
            with open(args.file, 'r') as f:
                targets = [line.strip() for line in f if line.strip()]
                if not targets:
                    print("错误: 目标文件为空")
                    if output_file:
                        output_file.close()
                    sys.exit(1)
        except FileNotFoundError:
            print(f"错误: 文件 {args.file} 未找到")
            if output_file:
                output_file.close()
            sys.exit(1)
    else:
        # 默认使用target.txt
        try:
            with open('target.txt', 'r') as f:
                targets = [line.strip() for line in f if line.strip()]
                if not targets:
                    print("错误: target.txt 文件为空, 请添加目标URL")
                    if output_file:
                        output_file.close()
                    sys.exit(1)
        except FileNotFoundError:
            print("错误: 未指定目标，请使用 -u 或 -f 参数, 或创建target.txt文件")
            if output_file:
                output_file.close()
            sys.exit(1)
    
    # 扫描所有目标
    for target in targets:
        check_vulnerability(target)
    
    # 关闭输出文件
    if output_file:
        output_file.close()
        print(f"[+] 结果已保存到文件: {args.output}")

if __name__ == "__main__":
    main()